#commons, holds a bunch of imports and stuff
from Loader import *#setup the loader before anything else
import os
import sys
import libtcodpy as L
import traceback
import string
VERSION = "A0.0.6a"#version number
MAP_WIDTH = getConfig("MAP WIDTH")#map constants, loaded from the configuration files
MAP_HEIGHT = getConfig("MAP HEIGHT")


#path of files
SAVEFILE_PATH = "saves/"
#L.console_set_custom_font("./tilesets/curses_square_16x16.bmp",L.FONT_LAYOUT_ASCII_INCOL)

#Tile constants
TILE_TILE = '~'
TILE_WALL = '#'
TILE_FLOOR = '.'
TILE_DOOR = '+'
TILE_SPACE = '`'
TILE_UP_STAIR = '<'
TILE_DOWN_STAIR = '>'
TILE_UP_DOWN_STAIR = 'X'


DIRECTIONS = [[-1,0],[-1,-1],[0,-1],[-1,1],[1,-1],[1,0],[1,1],[0,1]]#directional constants

#attachment constants
A_MAP = "Map"
A_MAP_VIEW = "Map Viewer"
A_UI_READER = "Keypress Reader"
A_RNG = "Random Number Generator"
A_UI_SWITCH = "User Interface Switcher"
#layers for the map
L_TILES = "Tile Layer"
L_CREATURES = "Creature Layer"
#UI constants for the UI switcher
UI_TITLE = "Title"
UI_KEY_BIND = "Key Bindings Screen"
UI_LOAD = "Load Screen"
UI_CHAR = "Direct control"
UI_MAP = "Map UI"
UI_SELECTOR = "Selection UI"
UI_NONE = ""

PLAYER_FACTION = 0#the player's faction

attachorder = [A_MAP,A_MAP_VIEW]

color_gold = L.Color(255,215,0)#gold
color_dark_purple = L.Color(102,0,102)

class Point:#point
	def __init__(self,x,y):
		self.x = x
		self.y = y
	def equals(self,otherpoint):
		return (self.x == otherpoint.x and self.y == otherpoint.y)

class Point3D:#a three dimensional point
	def __init__(self,x,y,z):
		self.x = x
		self.y = y
		self.z = z

class Rect:#rectangle
	def __init__(self, x, y, w, h):#make the x1 and x2 points of the room
		self.x1 = x
		self.y1 = y
		self.x2 = x+w
		self.y2 = y+h
		self.width = w
		self.height = h
	def center(self):#return the center of the room
		center_x = (self.x1+self.x2)/2
		center_y = (self.y1+self.y2)/2
		return (center_x, center_y)
	def intersect(self, other_rect):#returns True if this rectangle intersects with another
		return (self.x1 <= other_rect.x2 and self.x2 >= other_rect.x1 and
			self.y1 <= other_rect.y2 and self.y2 >= other_rect.y1)


global state
class State:#a different state/type of game
	attachments = dict()#the objects that are part of the state and can be refrenced with a call
	loadedfile = ""
	def __init__(self):#add attachments at the beginning
		pass
	def attach(self, refrence, attachment):
		self.attachments[refrence] = attachment
	def __getitem__(self,key):#use it like a dictionary
		return self.attachments[key]
	def __setitem__(self,key, value):
		self.attachments[key] = value
	def run(self):#run one 'tick' of the state
		pass
	def load(self,filename):#loads the game
		pass
	def save(self,filename):#saves the game
		if (os.path.isfile(filename)):
			os.remove(filename)
		fileObj = open(filename,"w")
		try:
			for thing in attachorder:
				fileObj.write(Game()[thing].save())
				fileObj.write("\\ENDSECTION\n")
			fileObj.close()
		except Exception:
			print "Error, could not save file!"
			traceback.print_exc()
			PauseConsoleErrorScreen("The file "+ str(filename) + " could not be saved.\n")
			fileObj.close()
			os.remove(filename)
		
def Game():#get the game
	global state
	return state
def setState(new_state):#change the state
	global state
	state = new_state

def viewtomapX(x):#change what is seen to the actual coordinate
	return Game()[A_MAP_VIEW].viewtomapX(x)
def viewtomapY(y):
	return Game()[A_MAP_VIEW].viewtomapY(y)



def SaveFileScreen():#a screen that allows the user to designate the filename
	filename = "test"
	endSaveScreen = False
	while (not endSaveScreen):
		
		L.console_set_background_color(0,L.black)
		L.console_set_foreground_color(0,L.white)
		L.console_clear(0)
		L.console_print_left(0,0,0,0,"What would you like to save the file as? (Press ESC to quit without saving.)")
		L.console_print_left(0,0,1,0,filename)
		L.console_flush()
		key = L.console_wait_for_keypress(True)
		#add characters
		if ((key.c >= ord('a') and key.c <= ord('z')) or (key.c >= ord('A') and key.c <= ord('Z')) or (key.c >= ord('0') and key.c <= ord('9'))):
			filename += chr(key.c)
		#backspace
		elif (key.vk == L.KEY_BACKSPACE):
			if (len(filename) > 0):
				filename = filename[:len(filename)-1]
		#done
		elif (key.vk == L.KEY_ENTER):
			if (len(filename) > 0):
				endSaveScreen = True
		elif (key.vk == L.KEY_ESCAPE):
			sys.exit(0)
	return filename
class MapLayer:#a layer of the map that is a bunch of tiles
	def __init__(self,xlength,ylength,fillobject=None):
		self.resizeLayer(xlength,ylength,fillobject)
	def resizeLayer(self,xlength,ylength,fillobject=None):#resizes the layer
		self.xlength = xlength
		self.ylength = ylength
		self.tiles = [[fillobject
			for y in range(self.ylength) ] 
				for x in range(self.xlength)]
	def __getitem__(self,key):
		return self.tiles[key]

class MapList:#a map that is a list, but can be referenced by points
	def __init__(self,xlength,ylength):
		self.resizeLayer(xlength,ylength)
		self.list = []
	def __getitem__(self,key):#NOTE: the key must be a point
		for item in self.list:
			if (item.x == key.x and item.y == key.y):
				return item
		#except KeyError#Note:brings an error
		return None
	def __iter__(self):
		return iter(self.list)
	def resizeLayer(self,xlength,ylength):
		self.xlength = xlength
		self.ylength = ylength

class UIReader:#reads keypresses, controls drawing, used for a specific "Game Mode"
	rendermap = True#does this state use the map, and render over it with displayOverView()?
	cansave = False#can we save on this screen?
	messages = []
	def receiveUI(self,keypressed,keyreleased,mouse):
		pass
	def displayOverView(self):#what is displayed over the view
		pass
	def basicUI(self,keypressed,keyreleased,mouse):#for UI that *ALWAYS* needs to be available, calls receiveUI from this
		if (keyCommand("Take Screenshot")):#save a screenshot
			for i in range(1000):
				filename = "./screenshots/pict"+str(i)+".png"
				if (not os.path.isfile(filename[2:])):#take off first two characters for python scan
					L.sys_save_screenshot(filename)
					print "Saved Screenshot on filename: " + filename[2:]
					return
			print "Error, all 1000 slots filled with screenshots"
			return
		if (keyCommand("Toggle Fullscreen")):#toggles fullscreen
			L.console_set_fullscreen(not L.console_is_fullscreen())
		self.receiveUI(keypressed,keyreleased,mouse)
	def getUIConstant(self):#get the UI type
		return UI_NONE
	def addMessage(self,message,color):#gives a message to the UI that it can display
		self.messages.append([message,color])

#Random Number Generator functions
class rng:#the random number generator class, a wrapper for the libtcod class
	def __init__(self, generator):
		self.generator = generator
	def nextInt(self, minimum, maximum):#get an integer between, and including, the minimum and maximum
		return L.random_get_int(self.generator, minimum, maximum)
	def nextFloat(self, minimum, maximum):#get a float between, and including, the minimum and maximum
		return L.random_get_float(self.generator, minimum, maximum)
	def nextGauss(self,minimum, maximum):#get a gaussian number between, and including, the minimum and maximum
		return L.random_get_gaussian_float(self.generator, minimum, maximum)
	def nextLuckyRNG(self, function, minimum, maximum, luck):#get an rng affected by luck, better luck means more chance of higher rolls
		rolls = []
		realluck = abs(int(luck))#the number of rerolls, can be good or bad
		if (self.nextFloat(0,1) < abs(luck)-realluck):#additional reroll
			realluck+=1
		for roll in range(realluck+1):#you get n+1 rolls
			rolls.append(function(minimum,maximum))
		rolls.sort()
		if (luck <= 0):
			return rolls[0]
		else:
			return rolls[len(rolls)-1]
	def coinflip(self):#coin flip function for a boolean
		return (self.nextInt(0,1) == 0)

#useful functions
def dist(x1, x2, y1, y2):#distance function
	return math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))

#chronos queue - used to handle the passage of time for something
class ChronosQueue:
	queue = dict()#the queue, where actions are stored
	time = 0L#time always starts at 0, save the difference between the time and the object position in the queue
	def addtoqueuewithdelay(self,theobject, delay):
		if (delay <= 0):#invalid delay value
			print "Error, object assigned to queue with a low delay, ignoring request"
			return
#		elif (delay == 0):#act IMMEDIATELY (use for turns that weren't completed, use rarely, but timestop is fun) XXX: Unnecessary at moment
#			self.notifyObject(theobject)
		newtime = self.time+delay
		theobject.assignedtime = newtime
		try:
			self.queue[newtime].append(theobject)
		except KeyError:
			self.queue[newtime] = [theobject]
	def addtoqueueabsolute(self,theobject, time):#add the object to the queue at an exact time
		if (time <= self.time):
			print "Error, object assigned to queue when it has passed the absolute time, ignoring request"
			return
		theobject.assignedtime = time
		try:
			self.queue[time].append(theobject)
		except KeyError:
			self.queue[time] = [theobject]
	def notifyObject(self,theobject):#notify the object
		if (not theobject.rejectnotifications):#can the object be notified?		
					self.addtoqueuewithdelay(theobject,int(theobject.notify()*theobject.delay))#notify the object, notify method should return # of turns for next action
	def removeObject(self,theobject):#remove the object from the queue
		if (not self.queue[theobject.assignedtime].__contains__(theobject)):
			print "ERROR: object not found in queue when removed!"
			return
		
		
	def tick(self):#advance the queue and notify any objects with the particular time
		self.time += 1
		if (self.queue.__contains__(self.time)):
			for theobject in self.queue[self.time]:	
				self.notifyObject(theobject)
			self.queue.pop(self.time)


class ChronosObject:#object affected by time, in a ChronosQueue
	rejectnotifications = False#is the object gone
	assignedtime = 0L
	delay = 100#the delay before the object acts again
	def notify(self):#The object is notified that it has a turn, return <1 for fast actions or >1 for slow ones
		print "Notified"
		return 1#normal turn



#go through the file and read the config files -- execute when the file starts -- LATER


#class MouseInterface:#controls the mouse
#	enabled = False
#	cursorx = 0
#	cursory = 0
